﻿using System;
using System.Collections.Generic;
using System.Linq;
using RayDen.Library.Components.Accelleration;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Scene;
using RayDen.SmallPT.Acceleration;
using RayDen.SmallPT.Light;
using TriangleMesh = RayDen.Library.Components.TriangleMesh;

namespace RayDen.SmallPT
{
    public class SceneManager : IMaterialProvider, IAccellerationStructure
    {

        private List<ILight> lights;
        private TriangleMesh[] geometry;
        private SceneGeometryInfo sceneGeometry;
        private Dictionary<string, MaterialInfo> materials;

        private IAccellerationStructure accel;
        private MaterialInfo defaultMaterial;

        public SceneManager(TriangleMesh[] geometry, SceneGeometryInfo sceneGeometry, MaterialInfo[] mats)
        {
            this.sceneGeometry = sceneGeometry;
            this.geometry = geometry;
            this.materials = mats.ToDictionary(item => item.Name.ToLower());
            var acc =
                //new OctreeAcceleration();
                //new BVHAccellerationStructure();
                //new BoundingVolumeHierarchyAcceleration();
                //new BottomUpBvhAccelleration();
                //new LinearBvhAccelleration();
                //new TopDownBvhAccell();
                new BoundingVolumeHierarchyAccelerationStructure();
            //new BruteForceAccellerationStructure();
            //new BspAccellerationStructure();
            acc.Init(sceneGeometry, this, geometry);
            this.accel = acc;
            this.lights = new List<ILight>();
            this.defaultMaterial = new MaterialInfo() { Kd = new RgbSpectrum(0.6f) };
            Console.WriteLine("Using {0} ", accel.GetType().Name);
        }

        public bool Intersect(RayInfo ray)
        {
            return accel.Intersect(ray);
        }

        public bool Intersect(RayInfo ray, out IntersectionInfo intersection)
        {
            return accel.Intersect(ray, out intersection);
        }

        public void AddLight(ILight light)
        {
            this.lights.Add(light);
        }

        #region IMaterialProvider Members
        public MaterialInfo[] GetAll()
        {
            return this.materials.Values.ToArray();
        }
        public MaterialInfo Get( int id )
        {
            throw new NotImplementedException();
        }
        public MaterialInfo Get(string name)
        {
            MaterialInfo result = null;
            if (materials.TryGetValue(name ?? "default", out result))
            {//.ToLower()
                return result;
            }
            return defaultMaterial;
        }

        #endregion

        #region IAccellerationStructure Members

        public void Init(SceneGeometryInfo scene, IMaterialProvider materialProvider, TriangleMesh[] geometry)
        {
            this.sceneGeometry = scene;
            this.geometry = geometry;
        }

        #endregion
    }
}